using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;

using UnityEngine;

namespace HNet
{
    public class ServerClass
    {
        //监听嵌套字

        public Socket listenfd;

        //客户端链接
        public Conn[] conns;

        //最大链接数
        public int maxConn = 10;

        public uint ConvIndex = 1;

        private int _LocalClinetConv = -99;

        private Action<byte[]> _localReceive;

        private IPEndPoint UDPServEndPoint;

        //获取链接池索引，返回负数表示获取失败
        private int NewIndex()
        {
            if (conns == null)
                return -1;
            for (int i = 0; i < conns.Length; i++)
            {
                if (conns[i] == null)
                {
                    conns[i] = new Conn();
                    return i;
                }
                else if (conns[i].IsUse == false)
                {
                    return i;
                }
            }
            return -1;
        }

        //开启服务器
        public bool Start(int port)
        {
            //链接池
            conns = new Conn[maxConn];
            for (int i = 0; i < maxConn; i++)
            {
                conns[i] = new Conn();
            }
            //Socket
            listenfd = new Socket(AddressFamily.InterNetwork,
                SocketType.Stream, ProtocolType.Tcp);
            //Bind
            IPAddress ipAdr = IPAddress.Any;//IPAddress.Parse(host);
            IPEndPoint ipEp = new IPEndPoint(ipAdr, port);
            UDPServEndPoint = new IPEndPoint(ipAdr, port);

            //取消本地接受客户端
            _LocalClinetConv = -99;

            try
            {
                listenfd.Bind(ipEp);
                //Listen

                listenfd.Listen(maxConn);
                //Accept
                listenfd.BeginAccept(TcpAcceptCb, this);

                string hostName = Dns.GetHostName();   //获取本机名
                IPHostEntry localhost = Dns.GetHostEntry(hostName);    //方法已过期，可以获取IPv4的地址
                string host = "未知";
                for (int i = localhost.AddressList.Length - 1; i >= 0; i--)
                {
                    if (localhost.AddressList[i].ToString().Contains("."))
                    {
                        host = localhost.AddressList[i].ToString();
                    }
                }

                NetLog.S("启动成功，IP:" + host);
                return true;
            }
            catch (SocketException e)
            {
                NetLog.S("启动失败，" + e);
                return false;
            }
        }

        //Accept回调
        private void TcpAcceptCb(IAsyncResult ar)
        {
            try
            {
                Socket socket = listenfd.EndAccept(ar);
                int index = NewIndex();
                ConvIndex = (uint)(index + 1);
                if (index < 0)
                {
                    socket.Close();
                    Console.Write("[服务器]链接已满");
                }
                else
                {
                    Conn conn = conns[index];
                    conn.Init(socket);

                    string adr = conn.GetAdress();

                    NetLog.S("客户端连接 " + adr + " conn池ID：" + index);

                    conn.socket.BeginReceive(conn.ReadBuff,
                        conn.BuffCount, conn.BuffRemain(),
                        SocketFlags.None, TcpReceiveCb, conn);

                    Byte[] conv = new Byte[4];
                    KCP.ikcp_encode32u(conv, 0, ConvIndex);
                    socket.Send(conv);

                    //建立UDP
                    IPAddress ipadr = IPAddress.Parse(adr.Split(':')[0]);
                    int port = int.Parse(adr.Split(':')[1]);
                    IPEndPoint clientEndPoint = new IPEndPoint(ipadr, port + 1);

                    UdpConnect(conn, clientEndPoint,
                        new IPEndPoint(UDPServEndPoint.Address, UDPServEndPoint.Port + (int)ConvIndex));
                }
                listenfd.BeginAccept(TcpAcceptCb, null);
            }
            catch (Exception e)
            {
                NetLog.S("AcceptCb连接中断:" + e.Message);
            }
        }

        private void TcpReceiveCb(IAsyncResult ar)
        {
            Conn conn = (Conn)ar.AsyncState;
            try
            {
                int count = conn.socket.EndReceive(ar);
                //关闭信号
                if (count <= 0)
                {
                    NetLog.S("收到 " + conn.GetAdress() + " 断开链接，正常退出");
                    conn.Close();
                    return;
                }
                //数据处理

                //conn.mRecvQueue.Push(conn.readBuff.Take(count).ToArray());

                //继续接收
                conn.socket.BeginReceive(conn.ReadBuff,
                    conn.BuffCount, conn.BuffRemain(),
                    SocketFlags.None, TcpReceiveCb, conn);
            }
            catch (Exception e)
            {
                NetLog.S("收到 " + conn.GetAdress() + " 断开链接，" + e);
                conn.Close();
            }
        }

        public void UdpConnect(Conn conn, IPEndPoint client, IPEndPoint serv)
        {
            conn.ClientEndPoint = client;
            conn.UDPClient = new UdpClient(serv);
            conn.UDPClient.Connect(conn.ClientEndPoint);

            conn.UDPClient.BeginReceive(ReceiveCallback, conn);
            conn.InitKcp(ConvIndex);
            NetLog.S("UDP监听" + serv + "，连接" + conn.ClientEndPoint);
        }

        private void ReceiveCallback(IAsyncResult ar)
        {
            Conn conn = (Conn)(ar.AsyncState);
            Byte[] data = (conn.IpEndPoint == null)
                ? conn.UDPClient.Receive(ref conn.IpEndPoint)
                : conn.UDPClient.EndReceive(ar, ref conn.IpEndPoint);

            //NetLog.S("UDP 收到消息" + data.Length);

            if (null != data)
                conn.RecvQueue.Push(data);

            if (conn.UDPClient != null)
            {
                // try to receive again.
                conn.UDPClient.BeginReceive(ReceiveCallback, conn);
            }
        }

        //发送数据
        public void Send(byte[] bytes, int[] receiverConvs)
        {
            try
            {
                //群发
                for (int j = 0; j < receiverConvs.Length && j < conns.Length; j++)
                {
                    int index = receiverConvs[j] - 1;
                    if (conns[index] == null)
                        continue;
                    if (!conns[index].IsUse)
                        continue;
                    //如果是本地客户端，直接投递
                    if (_LocalClinetConv == receiverConvs[j])
                    {
                        _localReceive(bytes);
                    }
                    else
                    {
                        conns[index].mKcp.Send(bytes);
                    }
                }
            }
            catch
            {
                // ignored
            }
        }

        public void Close()
        {
            listenfd.Close();

            for (int i = 0; i < conns.Length; i++)
            {
                if (conns[i] == null)
                    continue;
                if (!conns[i].IsUse)
                    continue;
                conns[i].socket.Close();
                conns[i].socket = null;
                conns[i].UDPClient.Close();
                conns[i].UDPClient = null;
            }
        }

        public void Update(uint current)
        {
            for (int i = 0; i < conns.Length; i++)
            {
                if (conns[i] == null)
                    continue;
                if (!conns[i].IsUse)
                    continue;

                //读取字节信息并转入KCP
                while (!conns[i].RecvQueue.Empty())
                {
                    var buf = conns[i].RecvQueue.Pop();

                    conns[i].mKcp.Input(buf);
                }

                for (var size = conns[i].mKcp.PeekSize(); size > 0; size = conns[i].mKcp.PeekSize())
                {
                    var buffer = new byte[size];
                    if (conns[i].mKcp.Recv(buffer) > 0)
                    {
                        //  Send(buffer);

                        string str = System.Text.Encoding.UTF8.GetString(buffer);
                        NetLog.S("收到(" + buffer.Length + "):" + str);
                        MessageDistribution(buffer);
                        //Debug.Log((UInt32)(Convert.ToInt64(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds) & 0xffffffff));
                    }
                }

                conns[i].mKcp.Update(current);
            }
        }

        private void MessageDistribution(byte[] buf)
        {
            Message msg = ProtocolBuffer.DeSerialize<Message>(buf);

            //根据接收者判断是否转发
            switch (msg.Receiver[0])
            {
                case Message.ServerIndex:

                    //服务器处理

                    break;

                case Message.AllClients:
                    List<int> allConv = new List<int>();
                    for (int j = 0; j < conns.Length; j++)
                    {
                        if (conns[j] == null)
                            continue;
                        if (!conns[j].IsUse)
                            continue;
                        allConv.Add((int)conns[j].Conv);
                    }

                    Send(buf, allConv.ToArray());
                    break;

                case Message.OtherClients:
                    List<int> allConvOther = new List<int>();
                    for (int j = 0; j < conns.Length; j++)
                    {
                        if (conns[j] == null)
                            continue;
                        if (!conns[j].IsUse)
                            continue;
                        if (conns[j].Conv == msg.Sender)
                        {
                            continue;
                        }
                        allConvOther.Add((int)conns[j].Conv);
                    }
                    Send(buf, allConvOther.ToArray());
                    break;

                default:
                    Send(buf, msg.Receiver);
                    break;
            }
        }

        public void SetLocalClient(int conv, Action<byte[]> localReceive)
        {
            _LocalClinetConv = conv;
            _localReceive = localReceive;
        }

        public void LocalHostDistribute(byte[] bytes)
        {
            MessageDistribution(bytes);
        }
    }
}